/*
 * -------------------------------------- 
 * FileName: 2.stack.aio.c 
 * Content:
 * 
 * Auther:  杨则瑞 
 * Encode:  utf-8 | CRLF                  
 * -------------------------------------- 
 */

#include<stdio.h>
#include<malloc.h>
#include<string.h>
#include<windows.h>
#include<memory.h>

/**
 * debugging 宏
 */
#ifdef DEBUG_MODE

    #ifdef _WIN32

        #include<windows.h>
        #define _debug(_msg,_type)\
            do{\
                MessageBeep(0);\
                MessageBox(NULL,_msg,_type,0); \
            }while(0)
        
    #endif

    #ifdef __linux__

        #define _debug(_msg,_type)\
            do{\
                fprintf(stderr,"[%s]:%s",_msg,_type)\
            }while(0)
    
    #endif

#else

    #define _debug(_msg,_type)\
    do {} while (0)

#endif


/**
 * stack 容器
 */
#define ns(title) stack_##title

typedef enum ns(error_t)
{
    ns(OK),
    ns(INSUFFICIENT_MEMORORY),
    ns(STACK_EMPTY)

} ns(error_t);


typedef struct ns(t)
{
    void * data;

    size_t _elemSize;
    size_t _len;
    size_t _capacity;
    
    ns(error_t) *_error_handler;

} ns(t);


void ns(init)  (ns(t) * self, size_t elemSize, ns(error_t) * error_handler);
void ns(uninit) ( ns(t) * self);


void *ns(top) (ns(t) * self);
void *ns(push_get) (ns(t) * self);
void ns(pop) (ns(t) * self);

void ns(reserve) (ns(t) * self, size_t new_len);
#undef ns  // 结束stack命名空间




/**
 * 双链表-队列 容器
 */
#define ns(lable) duList_##lable
typedef enum ns(error_t)
{
    ns(OK),
    ns(INSUFFICIENT_MEMORORY),
    ns(INDEX_OUT_OF_BOUND),

} ns(error_t);


typedef struct ns(node_t)  
{
    struct ns(node_t) *next;
    struct ns(node_t) *prev;
    int data[0];

} ns(node_t);


typedef struct ns(t)
{
    struct ns(node_t) *_header;
    int _elem_size;
    ns(error_t)* _error_handler;

} ns(t);



int ns(init)   (ns(t) * self, size_t elem_size, ns(error_t)* error_handler);
int ns(uninit) (ns(t) * self);

int ns(insert) (ns(t) * self, size_t pos,const void * element);
int ns(insert_reverse) (ns(t) * self, size_t pos, const void * element);
int ns(erase)  (ns(t) * self, size_t pos);
int ns(erase_reverse)  (ns(t) * self, size_t pos);
int ns(clear)  (ns(t) * self);

int    ns(is_empty) (const ns(t) * self);
size_t ns(length)   (const ns(t) * self);
int    ns(get)      (const ns(t) * self, size_t pos, void * val_dest);
int    ns(get_reverse) (const ns(t) * self, size_t pos, void * val_dest);

#undef ns //结束duList命名空间



/**
 * 主程序
 */

// 一张扑克牌的数据
typedef struct 
{
    int id;
    char name[16];
    char varient[16];
}poker_t;

//一副标准扑克牌(54张牌)
poker_t std_cards[];
//用于洗牌的随机数种子
int shuffle_sed = 6147;
//玩家人数
int players_cnt = 2;
//玩家们的牌
duList_t *cardsInPlayers = NULL;
//桌子上的牌
stack_t cardsOnTable;
//回合数
int rounds = 0;


poker_t std_cards[] = {
    {1,"Heart","Ace"},
    {2,"Heart","2"},
    {3,"Heart","3"},
    {4,"Heart","4"},
    {5,"Heart","5"},
    {6,"Heart","6"},
    {7,"Heart","7"},
    {8,"Heart","8"},
    {9,"Heart","9"},
    {10,"Heart","10"},
    {11,"Heart","Jack"},
    {12,"Heart","King"},
    {13,"Heart","Queen"},
    {14,"Spades","Ace"},
    {15,"Spades","2"},
    {16,"Spades","3"},
    {17,"Spades","4"},
    {18,"Spades","5"},
    {19,"Spades","6"},
    {20,"Spades","7"},
    {21,"Spades","8"},
    {22,"Spades","9"},
    {23,"Spades","10"},
    {24,"Spades","Jack"},
    {25,"Spades","King"},
    {26,"Spades","Queen"},
    {27,"Diamonds","Ace"},
    {28,"Diamonds","2"},
    {29,"Diamonds","3"},
    {30,"Diamonds","4"},
    {31,"Diamonds","5"},
    {32,"Diamonds","6"},
    {33,"Diamonds","7"},
    {34,"Diamonds","8"},
    {35,"Diamonds","9"},
    {36,"Diamonds","10"},
    {37,"Diamonds","Jack"},
    {38,"Diamonds","King"},
    {39,"Diamonds","Queen"},
    {40,"Clubs","Ace"},
    {41,"Clubs","2"},
    {42,"Clubs","3"},
    {43,"Clubs","4"},
    {44,"Clubs","5"},
    {45,"Clubs","6"},
    {46,"Clubs","7"},
    {47,"Clubs","8"},
    {48,"Clubs","9"},
    {49,"Clubs","10"},
    {50,"Clubs","Jack"},
    {51,"Clubs","King"},
    {52,"Clubs","Queen"},
    {53,"Joker","A"},
    {54,"Joker","B"}
};

//牌的数量
#define _cardset_size sizeof(std_cards)/sizeof(poker_t)
#define divider \
"======================================================================="


//根据玩家人数发牌
void cardDistribute();
//洗牌
void cardShuffler();
//初始化玩家的牌队列、桌子上的牌堆
void init();
//销毁化玩家的牌队列、桌子上的牌堆
void uninit();
//检查本回合的信息
void inspect();
//回顾游戏
void summary(char * comment);
//玩家采取行动
int playerTakeAction(int player_cnt);

int main(int argc, char * argv[])
{
    unsigned char running = 1;

    // 获取参数
    if(argc > 1)
    {
        int vio = sscanf(argv[1],"%d",&players_cnt);
        if(vio != 1)
        {
            printf("fatal: Invalid Player quantity: not a integer.\n");
            //玩家的数量是小于1的、或者比牌的数量都要多。
            running = 0;
            system("pause");
        }
        else if(players_cnt > _cardset_size || players_cnt < 1)
        {
            printf("fatal: Invalid Player quantity: not in range (1, %d).\n",_cardset_size);
            //玩家的数量是小于1的、或者比牌的数量都要多。
            running = 0;
            system("pause");
        }
        
    }

    if(argc > 2)
    {
        int vio = sscanf(argv[2],"%d",&shuffle_sed);
        if(vio != 1)
        {
            printf("fatal: Invalid shuffle seed: not a integer.\n");
            //输入的随机种子不是整数
            running = 0;
            system("pause");
        }
    }

    //初始化数据结构
    init();
    system("pause");
    system("cls");
    
    char gameResultBuffer[512];

    rounds = 0;

    //开始玩游戏
    if(running)
    {

        cardShuffler();

        cardDistribute();

        unsigned char playing = 1;
        

        while (playing)
        {
            ++ rounds;
         
            inspect();

            int rst;
            int i;
            for(i = 0; i < players_cnt; ++ i)
            {
                rst = playerTakeAction(i);   
                if(rst != 1)
                {
                    break;
                }
            }

            if(rst != 1)
            {
            
                if (rst == -1)
                {
                    sprintf(gameResultBuffer,"\nThe Game ends because it is unfair.\n");
                }
                else if (rst == 0)
                {
                    sprintf(gameResultBuffer,"\nplayer %d did't win.\n", i + 1);
                }

                summary(gameResultBuffer);
                break;

            }
            printf("\n\n" divider "\n");
            system("pause");
            system("cls");
        }
    }

    printf("press enter to terminate the program...");
    getchar();
    uninit();

    return 0;
}



const char *mots = 
    "====================================================================== \n"\
    "      Task 2_2 :          La Dache Emulator:                           \n"\
    "---------------------------------------------------------------------- \n"\
    "  Total Players : %d\n"\
    "  Shuffle Seed  : %u\n"\
    "  Usage: in terminal: \"[LaDache].exe <player counts> <ramdom seed>\"\n"
    "====================================================================== \n";

void init()
{

    stack_init(&cardsOnTable, sizeof(poker_t*), NULL);
    cardsInPlayers = (duList_t *)malloc(sizeof(duList_t) * players_cnt);
    for(int i = 0; i < players_cnt; ++ i)
    {
        duList_init(&cardsInPlayers[i],sizeof(poker_t*),NULL);
    }

    
    printf(mots, players_cnt, shuffle_sed);
    if( _cardset_size % players_cnt != 0)
    {
        printf(
            "Warn: Player quantity Can't divide cards quantity, the game might be unfair!\n"
        );
    }
}


void uninit()
{
    stack_uninit(&cardsOnTable);
    for(int i = 0; i < players_cnt; ++ i)
    {
        duList_uninit(&cardsInPlayers[i]);
    }
    free(cardsInPlayers);
}




void inspect()
{
    printf("\n\n===================[round inspection]================================== \n");

    printf( "  Rounds %d Starts!\n", rounds);

    printf("\n\n  Table Stack:\n");
    for(int i = 0; i < cardsOnTable._len; ++ i)
    {
        poker_t * p = ((poker_t **)cardsOnTable.data)[i]; 
        printf("[%s %s] ", p->name, p->varient);
    }

    for(int i =0; i < players_cnt; ++ i)
    {
        printf("\n\n  Player %d 's pocket:\n", i + 1);
        duList_node_t *p = (&cardsInPlayers[i])->_header->next;
        while (p != (&cardsInPlayers[i])->_header)
        {
            printf("[%s %s] ", (*(poker_t **)(p->data))->name, (*(poker_t **)(p->data))->varient);
            p = p->next;
        }
    }

    const char * end =
    "\n----------------------------------------------------------------------\n\n";
    printf(end);
}

void summary(char* comments)
{
    printf("\n\n===================[GAME  OVER]======================================== \n");
    printf(" Total Rounds: %d\n", rounds);
    printf(" %s",comments);
    printf("\n\n===================[GAME  OVER]======================================== \n");
}

int playerTakeAction(int player)
{
    poker_t * p = NULL;
    
    if(duList_is_empty(&cardsInPlayers[player]))
    {
        printf("player %d has no cards at first. unfair game!", player+1);
        return -1;
    }

    duList_get(&cardsInPlayers[player],0,&p);
    printf("player %d puts %s %s.\n", 1+player, p->name, p->varient);
    duList_erase(&cardsInPlayers[player],0);
    /*
        对桌子上的牌查重

        事实上这里可以使用键值对找到对应的
        牌的位置的，但是为了使用数据结构，
        采用了朴素的模拟思路。
    */
    int idx = -1;
    for(int j = 0; j < cardsOnTable._len; ++ j)
    {
        if(0==strcmp(((poker_t**)cardsOnTable.data)[j]->varient, p->varient))
        {
            idx = j;
        }
    }

    // 把自己手里的牌放下
    *(poker_t**)stack_push_get(&cardsOnTable) = p; 
    

    // 拉大车
    if(idx != -1)
    {
        printf("Player %d La Dache!\n", player + 1);
        while(cardsOnTable._len > idx)
        {
            poker_t* cardgot = *(poker_t **)stack_top(&cardsOnTable);
            printf(" > Get %s %s\n",cardgot->name,cardgot->varient);
            duList_insert_reverse(&cardsInPlayers[player],0,stack_top(&cardsOnTable));
            stack_pop(&cardsOnTable);
        }
        printf("Stop La Dache.\n", player + 1);
    }


    if(duList_is_empty(&cardsInPlayers[player]))
    {
        printf("GAME OVER : player %d Lose the game. game over!", player+1);
        return 0;
    }

    return 1;
}

void cardDistribute()
{
    int average_cnt = (_cardset_size + players_cnt) / players_cnt;
    for(int i = 0; i < _cardset_size; ++ i)
    {
        int playerio = i / average_cnt;
        poker_t * p = &std_cards[i];
        duList_insert(&cardsInPlayers[playerio],0,&p);
    }
} // 发牌


void cardShuffler()
{
    poker_t tmp;
    long long int scl = 12949213;
    long long int k = 1021;
    long long int l = 4091;
    long long int a = 4;
    long long int b = 2421;
    for(int _sf = 0; _sf < 21; ++ _sf)
    {
        for(int i = 0; i < _cardset_size; ++ i)
        {
            k = ((a * k * k % scl + b * k % scl + shuffle_sed) % scl + scl) % scl;
            l = ((a * l * l % scl + b * l % scl + shuffle_sed) % scl + scl) % scl;
            int sl = l % _cardset_size;
            int sk = k % _cardset_size;
            if (l != k)
            {
                tmp = std_cards[sl];
                std_cards[sl] = std_cards[sk];
                std_cards[sk] = tmp; 
            }
        }
    }

} // 随机交换数字






 /*
 * -------------------------------------- 
 * FileName: stack.c 
 * Content:
 *      sequential stack
 * Auther:  杨则瑞 
 * Encode:  utf-8 | CRLF                  
 * -------------------------------------- 
 */
#define ns(title) stack_##title

#define try_error(errors) \
do { \
    if(self->_error_handler != NULL)\
    { \
        *self->_error_handler = errors; \
    } \
} while (0)



const int stack_default_length = 80;
const int stack_dft_incri = 80;

void stack_reserve(stack_t * self, size_t new_len)
{

    if(new_len <= stack_default_length)
    {
        return;
    }

    void * tmp = realloc(self->data,new_len * self->_elemSize);

    if(tmp == NULL) 
    {

        try_error(stack_INSUFFICIENT_MEMORORY);

        _debug("Insufficient Memory","Error");

        return;
    }

    self->data = tmp;
    self->_capacity = new_len;
}

void stack_init( stack_t * self, size_t elemSize, stack_error_t * error_handler)
{
    self->_error_handler = error_handler;
    self->_elemSize = elemSize;
    self->_len = 0;
    self->_capacity = stack_default_length;

    self->data = malloc(self->_elemSize * self->_capacity);

    if(self->data == NULL) 
    {
        self->_capacity = 0;

        try_error(stack_INSUFFICIENT_MEMORORY);

        _debug("Insufficient Memory","Error");

        return;
    }

    return;
}

void stack_uninit( stack_t * self)
{
    self->_elemSize = 0;
    self->_len = 0;
    self->_capacity = 0;

    free(self->data);

    return;
}


void * stack_top(stack_t * self)
{
    if(0 == self->_len)
    {
        try_error(stack_STACK_EMPTY);

        _debug("Empty Stack","Error");
        
        return NULL;
    }

    return self->data + (self->_len - 1) * (self->_elemSize);
}




void * stack_push_get(stack_t * self)
{
    
    if(self->_len + 1 > self->_capacity) 
    {
        size_t tmp = self->_capacity;
        stack_reserve(self, self->_capacity + stack_dft_incri);
        if(self->_capacity == tmp)
        {
            
            try_error(stack_INSUFFICIENT_MEMORORY);

            _debug("Insufficient Memory","Error");

            return NULL;
        }

    }

    self->_len += 1;

    return self->data + (self->_len - 1) * (self->_elemSize);
}


void stack_pop(stack_t * self)
{
    if(self->_len <= 0)
    {
        try_error(stack_STACK_EMPTY);

        _debug("Empty Stack","Error");

    }

    if((self->_len - 1)/stack_dft_incri != (self->_len - 1)/stack_dft_incri) 
    {
        size_t tmp = self->_capacity;
        stack_reserve(self, self->_capacity - stack_dft_incri);
        if(self->_capacity == tmp)
        {
            
            try_error(stack_INSUFFICIENT_MEMORORY);

            _debug("Insufficient Memory","Error");
            
            return;
        }
    }

    self->_len -= 1;

    return;
}

#undef try_error
#undef ns 


/**
 * --------------------------------------
 * FileName: dual_list.c
 * Content: 
 *      双向链表
 * 
 * Auther:  杨则瑞
 * Encode:  utf-8 | CRLF
 * --------------------------------------
 */

#define ns(lable) duList_##lable

#define try_error(errors) \
do { \
    if(self->_error_handler != NULL)\
    { \
        *self->_error_handler = errors; \
    } \
} while (0)

// Memory
int ns(init) (ns(t) * self, size_t elem_size, ns(error_t)* error_handler)
{
    self->_elem_size = elem_size;
    self->_header = (ns(node_t) *)malloc(sizeof(ns(node_t)));
    if(error_handler != NULL)
    {
        self->_error_handler = error_handler;
    }

    if(self->_header == NULL)
    {
        _debug("Allocation Fault","WARN");

        try_error(duList_INSUFFICIENT_MEMORORY);

        return -1;
    }

    self->_header->next = self->_header;
    self->_header->prev = self->_header;
    return 0;
}


int ns(uninit) (ns(t) * self)
{
    ns(clear) (self);
    free(self->_header);
    return 0;
}

// Modification
int ns(insert) (ns(t) *self, size_t pos, const void * element)
{
    ns(node_t) * p = self->_header;

    for(int i = 0; i < pos; ++ i)
    {        
        p = p->next;

        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len].\nOperation Aborted","WARN");
            try_error(duList_INDEX_OUT_OF_BOUND);
            return -2;
        }


    }

    ns(node_t) *p_newnode = (ns(node_t) *)malloc(sizeof(ns(node_t)) + self->_elem_size);


    if(p_newnode == NULL)
    {
        _debug("Allocation Fault","WARN");
        try_error(duList_INSUFFICIENT_MEMORORY);

        return -1;
    }

    p->next->prev = p_newnode;
    p_newnode->next = p->next;
    p_newnode->prev = p;
    p->next = p_newnode;
    memcpy(p_newnode->data,element,self->_elem_size);

    return 0;
}

int ns(insert_reverse) (ns(t) * self, size_t pos, const void * element)
{
    
    
    ns(node_t) * p = self->_header;
    
    for(int i = 0; i < pos; ++ i)
    {
        p = p->prev;
        
        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len].\nOperation Aborted","WARN");
            try_error(duList_INDEX_OUT_OF_BOUND);

            return -2;
        }
    }

    ns(node_t) *p_newnode = (ns(node_t) *)malloc(sizeof(ns(node_t)) + self->_elem_size);


    if(p_newnode == NULL)
    {
        _debug("Allocation Fault","WARN");
        try_error(duList_INSUFFICIENT_MEMORORY);

        return -1;
    }

    p->prev->next = p_newnode;
    p_newnode->prev = p->prev;
    p->prev = p_newnode;
    p_newnode->next = p;

    memcpy(p_newnode->data,element,self->_elem_size);

    return 0;

}


int ns(erase)  (ns(t) * self, size_t pos)
{
     
    ns(node_t) * p = self->_header->next;
    
    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        try_error(duList_INDEX_OUT_OF_BOUND);

        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        
        if(p->next == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            try_error(duList_INDEX_OUT_OF_BOUND);

            return -2;
        }

        p = p->next;
        
    }

    p->prev->next = p->next;
    p->next->prev = p->prev;
    free(p);

    return 0;
}


int ns(erase_reverse)  (ns(t) * self, size_t pos)
{

    ns(node_t) * p = self->_header->prev;
    
    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        try_error(duList_INDEX_OUT_OF_BOUND);

        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        
        if(p->prev == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            return -2;
        }

        p = p->prev;
        
    }

    p->prev->next = p->next;
    p->next->prev = p->prev;
    free(p);

    return 0;
}


int ns(clear)  (ns(t) * self)
{
    ns(node_t) *p = self->_header->next;

    while (p != self->_header)
    {
        p->prev->next = p->next;
        p->next->prev = self->_header;
        free(p);
        p = self->_header->next;
    }

    return 0;
}

// Utility
int ns(is_empty) (const ns(t) * self)
{
    return self->_header->next == self->_header;
}

size_t ns(length)   (const ns(t) * self)
{
    ns(node_t) *p = self->_header->next;
    size_t cnt = 0;
    while (p != self->_header)
    {
        cnt ++;
        p = p->next;
    }
    
    return cnt;
}

int ns(get) (const ns(t) * self, size_t pos, void * val_dest)
{
    ns(node_t) * p = self->_header->next;

    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            return -2;
        }
        p = p->next;
    }
    
    memcpy(val_dest, p->data, self->_elem_size);

    return 0;
}


int ns(get_reverse) (const ns(t) * self, size_t pos, void * val_dest)
{
    
    ns(node_t) * p = self->_header->prev;

    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            return -2;
        }
        p = p->prev;
    }
    
    memcpy(val_dest, p->data, self->_elem_size);

    return 0;

}

#undef try_error
#undef ns 